#include "test.h"
#include "observer.h"
#include "singleton.h"
#include <fstream>
#include <string>

typedef unsigned char BYTE;

using namespace gl;
using namespace std;

class Publisher : public Observable
{
	public:
		Publisher(const char* name_, const char* addr_) :
			name(name_), addr(addr_) {}

		void ChangeName(const char* name_)
		{
			name = name_;

			SetChanged();
			Notify();
		}

		std::string name;
		std::string addr;
};

class Subscriber1 : public Observer<Publisher>
{
	public:
		virtual void Update(Publisher* p)
		{
			if (p)
			{
				std::cout << __PRETTY_FUNCTION__ 
					<< " | name=" << p->name << ", addr=" << p->addr
					<< std::endl;
			}
		}
};

class Subscriber2 : public Observer<Publisher>
{
	public:
		virtual void Update(Publisher* p)
		{
			if (p)
			{
				std::cout << __PRETTY_FUNCTION__ 
					<< " | name=" << p->name << ", addr=" << p->addr
					<< std::endl;
			}
		}
};

TEST_CASE(observer)
{
	Publisher* publisher = new Publisher("david", "zk.road.988");
	Subscriber1* sub1 = new Subscriber1();
	Subscriber2* sub2 = new Subscriber2();

	publisher->Attach(sub1);
	publisher->Attach(sub2);

	publisher->ChangeName("david++");

	publisher->Detach(sub1);

	publisher->ChangeName("jackie");
}

struct MsgProcessorBase
{
	virtual bool __doit(void* receiver, void* msg) = 0;
};

template <typename ReceiverType, typename MsgType>
struct MsgProcessor : public MsgProcessorBase
{
	virtual bool __doit(void* receiver, void* msg)
	{
		DoMsg((ReceiverType*)receiver, *((MsgType*)msg));
		return true;
	}

	virtual void DoMsg(ReceiverType* receiver, const MsgType& msg) = 0;
};

template <typename ReceiverType>
class MsgCenter
{
	public:
		void Reg(unsigned int cmd, unsigned int para, MsgProcessorBase* processor)
		{
			if (cmd < 255 && para < 255)
				processors_[cmd][para] = processor;
		}

		void Unreg(unsigned int cmd, unsigned int para)
		{
			if (cmd < 255 && para < 255)
				processors_[cmd][para] = NULL;
		}

		bool DoMsg(ReceiverType* receiver, unsigned int cmd, unsigned int para, void* msg)
		{
			if (cmd < 255 && para < 255)
				if (processors_[cmd][para])
					return processors_[cmd][para]->__doit((void*)receiver, msg);
			return false;
		}

		static MsgCenter<ReceiverType>& Instance()
		{
			static MsgCenter<ReceiverType> msgcenter;
			return msgcenter;
		}

	private:
		MsgCenter() 
		{
			std::memset(processors_, 0, sizeof(processors_));
		}

		MsgProcessorBase* processors_[256][256];
};

template <typename ReceiverType, typename MsgType>
struct __MsgCenterRegister
{
	__MsgCenterRegister(MsgProcessorBase* processor, MsgType dummymsg = MsgType())
	{
		MsgCenter<ReceiverType>::Instance().Reg(
				dummymsg.GetCmdType(), dummymsg.GetParaType(), processor);
	}
};

#define DO_MSG(RECEIVER, NS, MSG)					\
void Do##MSG(RECEIVER* receiver, const NS::MSG& msg); \
struct MSG##Processor : public MsgProcessor<RECEIVER, NS::MSG>\
{													\
	virtual void DoMsg(RECEIVER* receiver, const NS::MSG& msg)			\
	{												\
		Do##MSG(receiver, msg);								\
	}												\
};													\
static __MsgCenterRegister<RECEIVER, NS::MSG> __MsgCenterRegister_##MSG(new MSG##Processor());	\
void Do##MSG(RECEIVER* receiver, const NS::MSG& msg) 

///////////////////////////////////////////////

struct SceneUser
{
	char name[33];
};

namespace Cmd {

	struct TestCommand
	{
		BYTE GetCmdType() { return 1; }
		BYTE GetParaType() { return 1; }

		int arg1;
		int arg2;
		char name[33];
	};
}

DO_MSG(SceneUser, Cmd, TestCommand)
{
	cout << __PRETTY_FUNCTION__ << endl;

	if (receiver)
		cout << "Player:" << receiver->name << endl;
	cout << "Msg:" << msg.name << "," << msg.arg1 << "," << msg.arg2 << endl;
}

TEST_CASE(onlytest)
{
	//msgcenter.Reg(1, 1, new TestCmd());

	Cmd::TestCommand cmd;
	cmd.arg1 = 1;
	cmd.arg2 = 2;
	strncpy(cmd.name, "david", 32);

	SceneUser player;
	strncpy(player.name, "david", 32);

	//if (MsgCenter<SceneUser>::Instance().DoMsg(&player, 1, 1, (void*)&cmd))
	if (MsgCenter<SceneUser>::Instance().DoMsg(NULL, 1, 1, (void*)&cmd))
		cout << "ok !" << endl;
	else
		cout << "can't find processor !" << endl;

}

TEST_CASE(teststring)
{
	printf("%%\n");
	//printf("\%");
	cout <<"%%" << endl;
	cout <<"\%" << endl;
}


class ArgumentList;
template <typename Tag> struct ArgKeyWord;
template <typename Tag> struct Argument;

struct ArgumentBase {};

struct ArgumentList : public ArgumentBase
{
	public:
		typedef void* PointerType;
		typedef std::map<PointerType, ArgumentBase*> ArgumentCont;

		template <typename T>
			int operator [] (const ArgKeyWord<T>& ArgKeyWord) const
			{
				return 0;
			}

		void reg(void* id, ArgumentBase* argbase)
		{
			args_[id] = argbase;
		}

	private:
		ArgumentCont args_;
};

template <typename Tag>
struct Argument : public ArgumentBase
{
	typedef typename Tag::type type;

	const type& value;

	Argument(const type& val) : value(val) {}

	void* Id()
	{
		return (void*)&ArgKeyWord<Tag>::instance;
	}

	template <typename Tag2>
		ArgumentList operator,(const Argument<Tag2>& rhs) const
		{
			ArgumentList arglist;
			arglist.reg(this->Id(), this);
			arglist.reg(rhs->Id(), &rhs);

			return arglist;
		}
};


template <typename Tag>
struct ArgKeyWord
{
	typedef typename Tag::type type;

	static ArgKeyWord<Tag> const instance;
	const Argument<Tag> operator = (const type& value) const
	{
		return Argument<Tag>(value);
	}	
};

template <typename Tag>
ArgKeyWord<Tag> const ArgKeyWord<Tag>::instance = {};


struct tag_name
{
	typedef std::string type;
};

namespace {	
	static ArgKeyWord<tag_name> const& name = ArgKeyWord<tag_name>::instance;
}


struct tag_id
{
	typedef unsigned int type;
};

namespace {	
	static ArgKeyWord<tag_id> const& id = ArgKeyWord<tag_id>::instance;
}

void fun(const ArgumentList& args)
{
	cout << args[name] << endl;
	cout << args[id] << endl;
}

TEST_CASE(arguments)
{
	cout << (name="aaaaaaa").value << endl;
	cout << (id=222).value << endl;

	//cout << (name="David", id=100) << endl;
}

RUN_TEST_SUIT()
